﻿module Commands

open System
open System.IO
open System.Collections.Generic
open System.Collections.ObjectModel


type StringCommand = {
   Name : string
   Value : string
   }


let ReadCommand(index : int, argv : string[]) =
   if (argv.Length - index = 0) then
      (0, None)
   else
      let name = argv.[index + 0]

      if (name.[0] <> '-') then
         failwithf "Missing '-' at beginning of command at: '%A'" name
      else         
         let needsValue = name.[name.Length - 1] = ':';

         if (needsValue && argv.Length - index < 2) then
            failwithf "Missing value for command: '%A'" name
         elif (needsValue) then
            let value = argv.[index + 1]

            (2, Some({Name = name; Value = value}))
         else
            (1, Some({Name = name; Value = "true"}))


let ReadCommands(argv : string[]) =
   let rec loop(i : int, found : StringCommand list ) =
      let (taken, command) = ReadCommand(i, argv)

      if (taken = 0) then
         found
      else         
         loop(i + taken, command.Value :: found)

   loop(0, List.Empty) 
      |> List.rev 
      |> List.toArray
         

let UpdateCommands(existing : StringCommand[], updates : StringCommand[]) =         
   let existed = existing |> Array.map(fun c -> (c.Name, c)) |> dict
   let updated = updates |> Array.map(fun c -> (c.Name, c)) |> dict

   if (existed.Count < existing.Length || updated.Count < updates.Length) then
      failwith "Duplicate commands detected"

   existed 
      |> Seq.map(fun c -> if (updated.ContainsKey(c.Key)) then updated.[c.Key] else c.Value) 
      |> Seq.toArray
   

// TODO tests
let FindCommand(name : string, commands : StringCommand[]) =
   let found = commands |> Array.filter(fun c -> c.Name = name)

   if (found.Length < 1) then
      failwithf "Missing argument '%A'" name

   if (found.Length > 1) then
      failwithf "Argument redefined '%A'" name

   found.[0]


// TODO tests
let TryFindCommand(name : string, commands : StringCommand[], fallback : string) =
   let found = commands |> Array.filter(fun c -> c.Name = name)

   if (found.Length < 1) then
      {StringCommand.Name = name; Value = fallback}
   else
      found.[0]
